package com.xlh.virtualization.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.xlh.aop.PreAcquireLock;
import com.xlh.enums.course.ChapterEnum;
import com.xlh.exception.common.GlobalException;
import com.xlh.exception.common.NotFoundException;
import com.xlh.pojo.course.ChapterBasic;
import com.xlh.service.course.ChapterExtendService;
import com.xlh.service.course.ChapterService;
import com.xlh.virtualization.dao.ImageAllocationMapper;
import com.xlh.virtualization.dao.ImageAllocationSlaveMapper;
import com.xlh.virtualization.dao.ImageMapper;
import com.xlh.virtualization.dao.ext.ImageAllocationMapperExt;
import com.xlh.virtualization.dao.ext.ImageAllocationSlaveMapperExt;
import com.xlh.virtualization.dao.helper.ContainerMapperHelper;
import com.xlh.virtualization.dao.helper.ImageAllocationMapperHelper;
import com.xlh.virtualization.dao.helper.ImageAllocationSlaveMapperHelper;
import com.xlh.virtualization.dao.helper.ImageMapperHelper;
import com.xlh.virtualization.domain.Container;
import com.xlh.virtualization.domain.Image;
import com.xlh.virtualization.domain.ImageAllocation;
import com.xlh.virtualization.domain.ImageAllocationSlave;
import com.xlh.virtualization.po.FlavorUpdatePO;
import com.xlh.virtualization.po.ImageAllocationImageUpdatePO;
import com.xlh.virtualization.po.ImageAllocationUpdatePO;
import com.xlh.virtualization.service.ImageAllocationService;
import com.xlh.virtualization.vo.FlavorVO;
import com.xlh.virtualization.vo.ImageAllocationCourseVO;
import com.xlh.virtualization.vo.ImageAllocationTableVO;
import com.xlh.virtualization.vo.ImageAllocationVO;
import com.xlh.virtualization.websocket.CourseExperimentWebsocket;
import com.xlh.virtualization.websocket.TrainingExperimentWebsocket;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xlh.virtualization.constant.ContainerRoleEnum.SLAVE;
import static com.xlh.virtualization.constant.ContextTypeEnum.TASK;
import static com.xlh.virtualization.constant.ContextTypeEnum.TRAINING;
import static com.xlh.virtualization.constant.ExperimentConstant.CLUSTER;
import static com.xlh.virtualization.constant.ExperimentConstant.SINGLE_NODE;
import static com.xlh.virtualization.constant.ImageAllocationConstant.IMAGE_ALLOCATION_KEY;
import static com.xlh.virtualization.constant.ImageConstant.SYSTEM_PRESET_CODE;

/**
 * @author cheer
 */
@Service
public class ImageAllocationServiceImpl implements ImageAllocationService {

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private ChapterExtendService chapterExtendService;

    @Autowired
    private ImageAllocationMapperHelper imageAllocationMapperHelper;

    @Autowired
    private ImageAllocationSlaveMapperHelper imageAllocationSlaveMapperHelper;

    @Autowired
    private ImageAllocationMapper imageAllocationMapper;

    @Autowired
    private ImageMapperHelper imageMapperHelper;

    @Autowired
    private ImageAllocationSlaveMapper imageAllocationSlaveMapper;

    @Autowired
    private ImageAllocationMapperExt imageAllocationMapperExt;

    @Autowired
    private ImageAllocationSlaveMapperExt imageAllocationSlaveMapperExt;

    @Autowired
    private ContainerMapperHelper containerMapperHelper;

    @Override
    public FlavorVO getFlavorByContext(Integer contextType, Long contextId) {
        ImageAllocation imageAllocation = imageAllocationMapperHelper.getByContext(contextType, contextId);
        if (imageAllocation == null) {
            throw new GlobalException("镜像未分配，无法更改配置");
        }
        return FlavorVO.builder()
                .mode(imageAllocation.getMode())
                .cpu(imageAllocation.getCpu())
                .ram(imageAllocation.getRam())
                .slaveCpu(imageAllocation.getSlaveCpu())
                .slaveRam(imageAllocation.getSlaveRam())
                .build();
    }

    @Override
    @PreAcquireLock(value = IMAGE_ALLOCATION_KEY)
    public void updateFlavorByContext(Integer contextType, Long contextId, FlavorUpdatePO flavorUpdatePO) {
        ImageAllocation imageAllocation = imageAllocationMapperHelper.getByContext(contextType, contextId);
        if (imageAllocation == null) {
            throw new GlobalException("镜像未分配，无法更改配置");
        }

        if (CLUSTER == imageAllocation.getMode()) {
            if (flavorUpdatePO.getSlaveCpu() == null) {
                throw new GlobalException("从节点cpu核数不能为空");
            }
            if (flavorUpdatePO.getSlaveRam() == null) {
                throw new GlobalException("从节点内存大小不能为空");
            }
        }

        if (Objects.equals(imageAllocation.getCpu(), flavorUpdatePO.getCpu()) &&
                Objects.equals(imageAllocation.getRam(), flavorUpdatePO.getRam())) {
            if (SINGLE_NODE == imageAllocation.getMode()) {
                return;
            } else if (CLUSTER == imageAllocation.getMode()) {
                if (Objects.equals(imageAllocation.getSlaveCpu(), flavorUpdatePO.getSlaveCpu())
                        && Objects.equals(imageAllocation.getSlaveRam(), flavorUpdatePO.getSlaveRam())) {
                    return;
                }
            }
        }

        ImageAllocation updateImageAllocation = ImageAllocation.builder()
                .id(imageAllocation.getId())
                .cpu(flavorUpdatePO.getCpu())
                .ram(flavorUpdatePO.getRam())
                .slaveCpu(CLUSTER == imageAllocation.getMode() ? flavorUpdatePO.getSlaveCpu() : null)
                .slaveRam(CLUSTER == imageAllocation.getMode() ? flavorUpdatePO.getSlaveRam() : null)
                .build();
        imageAllocationMapper.updateByPrimaryKeySelective(updateImageAllocation);

        if (Objects.equals(contextType, TASK.getCode())) {
            Map<String, Object> map = chapterExtendService.getCourseInfo(contextId);
            if (map != null && map.get("courseImageLoaded") != null && !(boolean) map.get("courseImageLoaded")) {
                CourseExperimentWebsocket.notifyUpdateFlavor(contextId);
            }
        } else if (Objects.equals(contextType, TRAINING.getCode())) {
            TrainingExperimentWebsocket.notifyUpdateFlavor(contextId);
        }
    }

    @Override
    public ImageAllocationVO getImageAllocation(Integer contextType, Long contextId) {
        // 查找单节点或主节点镜像分配
        ImageAllocation imageAllocation = imageAllocationMapperHelper.getByContext(contextType, contextId);
        if (imageAllocation == null) {
            return ImageAllocationVO.builder()
                    .allocated(false)
                    .build();
        }

        // 构建返回对象
        ImageAllocationVO imageAllocationVO = ImageAllocationVO.builder()
                .allocated(true)
                .mode(imageAllocation.getMode())
                .cpu(imageAllocation.getCpu())
                .ram(imageAllocation.getRam())
                .slaveCpu(imageAllocation.getSlaveCpu())
                .slaveRam(imageAllocation.getSlaveRam())
                .build();

        // 查找从节点镜像分配
        List<ImageAllocationSlave> imageAllocationSlaves = null;
        if (imageAllocation.getMode() == CLUSTER) {
            imageAllocationSlaves = imageAllocationSlaveMapperHelper.listByContext(contextType, contextId);
        }

        // 查找镜像
        Set<Long> imageIds = Sets.newHashSet(imageAllocation.getImageId());
        if (imageAllocationSlaves != null) {
            imageIds.addAll(imageAllocationSlaves.stream().map(ImageAllocationSlave::getImageId)
                    .collect(Collectors.toList()));
        }
        List<Image> images = imageMapperHelper.listByIds(Lists.newArrayList(imageIds));
        Map<Long, Image> imageMap = new HashMap<>();
        for (Image image : images) {
            imageMap.put(image.getId(), image);
        }

        // 单节点或主节点
        ImageAllocationVO.ImageCascader imageCascader = new ImageAllocationVO.ImageCascader();
        Image image = imageMap.get(imageAllocation.getImageId());
        if (image != null) {
            imageCascader.setImageSource(image.getImageSource());
            imageCascader.setImageId(image.getId());
        }
        imageAllocationVO.setImage(imageCascader);

        // 从节点
        if (imageAllocationSlaves != null) {
            imageAllocationVO.setSlaveSize(imageAllocationSlaves.size());
            List<ImageAllocationVO.ImageCascader> imageCascaders = new ArrayList<>();
            for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
                ImageAllocationVO.ImageCascader slaveImageCascader = new ImageAllocationVO.ImageCascader();
                Image slaveImage = imageMap.get(imageAllocationSlave.getImageId());
                if (slaveImage != null) {
                    slaveImageCascader.setImageSource(slaveImage.getImageSource());
                    slaveImageCascader.setImageId(slaveImage.getId());
                }
                imageCascaders.add(slaveImageCascader);
            }
            imageAllocationVO.setSlaveImages(imageCascaders);
        }

        return imageAllocationVO;
    }

    @Override
    @Transactional
    @PreAcquireLock(value = IMAGE_ALLOCATION_KEY)
    public void updateImageAllocation(Integer contextType, Long contextId, ImageAllocationUpdatePO imageAllocationUpdatePO) {
        if (CLUSTER == imageAllocationUpdatePO.getMode()) {
            if (imageAllocationUpdatePO.getSlaveCpu() == null) {
                throw new GlobalException("从节点cpu核数不能为空");
            }
            if (imageAllocationUpdatePO.getSlaveRam() == null) {
                throw new GlobalException("从节点内存大小不能为空");
            }
            if (CollectionUtils.isEmpty(imageAllocationUpdatePO.getSlaveImageIds())) {
                throw new GlobalException("从节点镜像Id不能为空");
            }
            for (Long slaveId : imageAllocationUpdatePO.getSlaveImageIds()) {
                if (slaveId == null) {
                    throw new GlobalException("从节点镜像Id不能为空");
                }
            }
        }

        ImageAllocation imageAllocation = imageAllocationMapperHelper.getByContext(contextType, contextId);
        if (imageAllocation == null) {
            insertImageAllocation(contextType, contextId, imageAllocationUpdatePO);
            if (CLUSTER == imageAllocationUpdatePO.getMode()) {
                imageAllocationSlaveMapperHelper.deleteByContext(contextType, contextId);
                insertImageAllocationSlave(contextType, contextId, imageAllocationUpdatePO.getSlaveImageIds());
            }
        } else {
            updateImageAllocation(imageAllocation, imageAllocationUpdatePO);
            if (SINGLE_NODE == imageAllocation.getMode() && CLUSTER == imageAllocationUpdatePO.getMode()) {
                insertImageAllocationSlave(contextType, contextId, imageAllocationUpdatePO.getSlaveImageIds());
            } else if (CLUSTER == imageAllocation.getMode() && SINGLE_NODE == imageAllocationUpdatePO.getMode()) {
                imageAllocationSlaveMapperHelper.deleteByContext(contextType, contextId);
            } else if (CLUSTER == imageAllocation.getMode() && CLUSTER == imageAllocationUpdatePO.getMode()) {
                updateImageAllocationSlave(contextType, contextId, imageAllocationUpdatePO.getSlaveImageIds());
            }
        }
    }

    @Override
    @Transactional
    @PreAcquireLock(value = IMAGE_ALLOCATION_KEY)
    public void deleteImageAllocation(Integer contextType, Long contextId) {
        imageAllocationMapperHelper.deleteByContext(contextType, contextId);
        imageAllocationSlaveMapperHelper.deleteByContext(contextType, contextId);
    }

    @Override
    @PreAcquireLock(value = IMAGE_ALLOCATION_KEY)
    public void updateImage(Integer contextType, Long contextId, ImageAllocationImageUpdatePO imageAllocationImageUpdatePO) {
        Container container = containerMapperHelper.getContainerById(imageAllocationImageUpdatePO.getContainerId());
        if (container == null) {
            throw new GlobalException("虚拟机不存在");
        }

        if (Objects.equals(SLAVE.getCode(), container.getContainerRole())) {
            imageAllocationSlaveMapperHelper.updateImage(contextType, contextId, container.getRoleNumber(),
                    imageAllocationImageUpdatePO.getImageId());
        } else {
            imageAllocationMapperHelper.updateImage(contextType, contextId, imageAllocationImageUpdatePO.getImageId());
        }
    }

    @Override
    public List<ImageAllocationCourseVO> listCourseImageAllocation(Long courseId) {
        // 查询章节信息
        List<ChapterBasic> chapterBasicList;
        try {
            chapterBasicList = chapterService.getChapterBasic(courseId, null, null, ChapterEnum.TASK);
        } catch (NotFoundException e) {
            return new ArrayList<>();
        }

        // 查询镜像分配
        List<Long> chapterIds = chapterBasicList.stream().map(ChapterBasic::getId).collect(Collectors.toList());
        List<ImageAllocation> imageAllocations = imageAllocationMapperHelper.listByContext(TASK.getCode(), chapterIds);
        Map<Long, ImageAllocation> imageAllocationMap = new HashMap<>();
        for (ImageAllocation imageAllocation : imageAllocations) {
            imageAllocationMap.put(imageAllocation.getContextId(), imageAllocation);
        }

        // 查询从节点镜像分配
        List<ImageAllocationSlave> imageAllocationSlaves = imageAllocationSlaveMapperHelper.listByContext(TASK.getCode(), chapterIds);
        Map<Long, List<ImageAllocationSlave>> imageAllocationSlaveMap = new HashMap<>();
        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
            if (imageAllocationSlaveMap.get(imageAllocationSlave.getContextId()) == null) {
                List<ImageAllocationSlave> imageAllocationSlaveList = new ArrayList<>();
                imageAllocationSlaveList.add(imageAllocationSlave);
                imageAllocationSlaveMap.put(imageAllocationSlave.getContextId(), imageAllocationSlaveList);
            } else {
                imageAllocationSlaveMap.get(imageAllocationSlave.getContextId()).add(imageAllocationSlave);
            }
        }

        // 查询镜像列表
        Set<Long> imageIds = new HashSet<>();
        imageIds.addAll(imageAllocations.stream().map(ImageAllocation::getImageId)
                .collect(Collectors.toList()));
        imageIds.addAll(imageAllocationSlaves.stream().map(ImageAllocationSlave::getImageId)
                .collect(Collectors.toList()));
        List<Image> imageList = imageMapperHelper.listByIds(Lists.newArrayList(imageIds));
        Map<Long, Image> imageMap = new HashMap<>();
        for (Image image : imageList) {
            imageMap.put(image.getId(), image);
        }

        // 构建返回对象
        List<ImageAllocationCourseVO> imageAllocationCourseVOS = new ArrayList<>();
        for (ChapterBasic chapterBasic : chapterBasicList) {
            ImageAllocationCourseVO imageAllocationCourseVO = buildImageAllocationCourseVOS(chapterBasic, imageAllocationMap,
                    imageAllocationSlaveMap, imageMap);
            imageAllocationCourseVOS.add(imageAllocationCourseVO);
        }
        return imageAllocationCourseVOS;
    }

    @Override
    public Boolean isImageAllocated(Long imageId) {
        return imageAllocationMapperExt.getByImageIdLimit1(imageId) != null ||
                imageAllocationSlaveMapperExt.getByImageIdLimit1(imageId) != null;
    }

    @Override
    public void copyImageAllocation(Integer contextType, Long sourceContextId, Long targetContextId) {
        ImageAllocation imageAllocation = imageAllocationMapperHelper.getByContext(contextType, sourceContextId);
        if (imageAllocation == null) {
            return;
        }

        ImageAllocation insertImageAllocation = ImageAllocation.builder()
                .contextType(contextType)
                .contextId(targetContextId)
                .mode(imageAllocation.getMode())
                .imageId(imageAllocation.getImageId())
                .cpu(imageAllocation.getCpu())
                .ram(imageAllocation.getRam())
                .gpuPercent(imageAllocation.getGpuPercent())
                .slaveCpu(imageAllocation.getSlaveCpu())
                .slaveRam(imageAllocation.getSlaveRam())
                .build();
        imageAllocationMapper.insertSelective(insertImageAllocation);

        if (imageAllocation.getMode() == CLUSTER) {
            List<ImageAllocationSlave> imageAllocationSlaves = imageAllocationSlaveMapperHelper.listByContext(contextType, sourceContextId);
            if (CollectionUtils.isNotEmpty(imageAllocationSlaves)) {
                for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
                    ImageAllocationSlave insertImageAllocationSlave = ImageAllocationSlave.builder()
                            .contextType(contextType)
                            .contextId(targetContextId)
                            .imageId(imageAllocationSlave.getImageId())
                            .roleNumber(imageAllocationSlave.getRoleNumber())
                            .build();
                    imageAllocationSlaveMapper.insertSelective(insertImageAllocationSlave);
                }
            }
        }
    }

    @Override
    public void trueDeleteImageAllocation(Integer contextType, Long contextId) {
        imageAllocationMapperHelper.trueDeleteByContext(contextType, contextId);
        imageAllocationSlaveMapperHelper.trueDeleteByContext(contextType, contextId);
    }

    @Autowired
    private ImageMapper imageMapper;

    @Override
    public void transport(Integer contextType, JdbcTemplate jdbcTemplate, Map<Long, Long> courseIdMap, Map<Long, Long> insertChapterMap, Map<Integer, Integer> iconMap) {
        String sql = "select " +
                "id, context_type, context_id, mode, image_id, cpu, ram, slave_cpu, slave_ram, gpu_percent " +
                "from vm_image_allocation " +
                "where is_deleted = 0 and context_type = (:contextType) and context_id in (:chapterIds)";

        // 取出远程数据库的章节id
        List<Long> chapterIds = Lists.newArrayList(insertChapterMap.keySet());
        if (CollectionUtils.isEmpty(chapterIds)) {
            return;
        }

        Map<String, Object> paramMap = Maps.newHashMap();
        paramMap.put("contextType", contextType);
        paramMap.put("chapterIds", chapterIds);

        RowMapper<ImageAllocation> rowMapper = new BeanPropertyRowMapper<>(ImageAllocation.class);

        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        List<ImageAllocation> imageAllocationList = namedParameterJdbcTemplate.query(sql, paramMap, rowMapper);

        if (CollectionUtils.isEmpty(imageAllocationList)) {
            return;
        }

        sql = "select " +
                "id, context_type, context_id, image_id, role_number " +
                "from vm_image_allocation_slave " +
                "where is_deleted = 0 and context_type = (:contextType) and context_id in (:chapterIds)";

        RowMapper<ImageAllocationSlave> rowMapperSlave = new BeanPropertyRowMapper<>(ImageAllocationSlave.class);

        List<ImageAllocationSlave> imageAllocationSlaveList = namedParameterJdbcTemplate.query(sql, paramMap, rowMapperSlave);
        Map<Long, List<ImageAllocationSlave>> imageAllocationSlaveMap = imageAllocationSlaveList.stream().collect(
                Collectors.groupingBy(ImageAllocationSlave::getContextId));

        Set<Long> imageIdSet = new HashSet<>();
        for (ImageAllocation imageAllocation : imageAllocationList) {
            imageIdSet.add(imageAllocation.getImageId());
        }
        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaveList) {
            imageIdSet.add(imageAllocationSlave.getImageId());
        }

        sql = "select " +
                "id, image_name, icon, description, image_name_in_docker, gpu " +
                "from vm_image " +
                "where is_deleted = 0 and id in (:imageIds)";

        paramMap.put("imageIds", Lists.newArrayList(imageIdSet));

        RowMapper<Image> rowMapperImage = new BeanPropertyRowMapper<>(Image.class);

        List<Image> imageList = namedParameterJdbcTemplate.query(sql, paramMap, rowMapperImage);
        Map<Long, Image> imageMap = imageList.stream().collect(
                Collectors.toMap(Image::getId, Function.identity()));


        for (ImageAllocation imageAllocation : imageAllocationList) {
            Long target = insertChapterMap.get(imageAllocation.getContextId());
            if (target != null) {
                Image image = imageMap.get(imageAllocation.getImageId());
                if (image != null) {
                    insertImage(image, iconMap);
                    Image newImage = imageMapperHelper.getByImageNameInDocker(image.getImageNameInDocker());
                    if (newImage != null) {
                        List<ImageAllocationSlave> imageAllocationSlaves = imageAllocationSlaveMap.get(imageAllocation.getContextId());
                        if (imageAllocationSlaves != null) {
                            for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
                                Image slaveImage = imageMap.get(imageAllocationSlave.getImageId());
                                if (slaveImage != null) {
                                    insertImage(slaveImage, iconMap);
                                    Image newSlaveImage = imageMapperHelper.getByImageNameInDocker(slaveImage.getImageNameInDocker());
                                    if (newSlaveImage != null) {
                                        imageAllocationSlave.setId(null);
                                        imageAllocationSlave.setContextId(target);
                                        imageAllocationSlave.setImageId(newSlaveImage.getId());
                                        imageAllocationSlaveMapper.insertSelective(imageAllocationSlave);
                                    }
                                }
                            }
                        }
                        imageAllocation.setId(null);
                        imageAllocation.setContextId(target);
                        imageAllocation.setImageId(newImage.getId());
                        imageAllocationMapper.insertSelective(imageAllocation);
                    }
                }
            }

        }
    }

    @Override
    public Map<Long, ImageAllocationTableVO> getTable(Integer contextType, List<Long> contextIds) {
        if (CollectionUtils.isEmpty(contextIds)) {
            return new HashMap<>();
        }

        List<ImageAllocation> imageAllocations = imageAllocationMapperHelper.listByContext(contextType, contextIds);
        Map<Long, ImageAllocation> imageAllocationMap = new HashMap<>();
        for (ImageAllocation imageAllocation : imageAllocations) {
            imageAllocationMap.put(imageAllocation.getContextId(), imageAllocation);
        }

        // 查询从节点镜像分配
        List<ImageAllocationSlave> imageAllocationSlaves = imageAllocationSlaveMapperHelper.listByContext(contextType, contextIds);
        Map<Long, List<ImageAllocationSlave>> imageAllocationSlaveMap = new HashMap<>();
        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
            if (imageAllocationSlaveMap.get(imageAllocationSlave.getContextId()) == null) {
                List<ImageAllocationSlave> imageAllocationSlaveList = new ArrayList<>();
                imageAllocationSlaveList.add(imageAllocationSlave);
                imageAllocationSlaveMap.put(imageAllocationSlave.getContextId(), imageAllocationSlaveList);
            } else {
                imageAllocationSlaveMap.get(imageAllocationSlave.getContextId()).add(imageAllocationSlave);
            }
        }

        // 查询镜像列表
        Set<Long> imageIds = new HashSet<>();
        imageIds.addAll(imageAllocations.stream().map(ImageAllocation::getImageId)
                .collect(Collectors.toList()));
        imageIds.addAll(imageAllocationSlaves.stream().map(ImageAllocationSlave::getImageId)
                .collect(Collectors.toList()));
        List<Image> imageList = imageMapperHelper.listByIds(Lists.newArrayList(imageIds));
        Map<Long, Image> imageMap = new HashMap<>();
        for (Image image : imageList) {
            imageMap.put(image.getId(), image);
        }

        // 构建返回对象
        List<ImageAllocationTableVO> imageAllocationTableVOS = new ArrayList<>();
        for (Long contextId : contextIds) {
            if (imageAllocationMap.containsKey(contextId)) {
                ImageAllocationTableVO imageAllocationTableVO = ImageAllocationTableVO.builder()
                        .contextId(contextId)
                        .images(new ArrayList<>())
                        .flavors(new ArrayList<>())
                        .build();

                ImageAllocation imageAllocation = imageAllocationMap.get(contextId);
                imageAllocationTableVO.setMode(imageAllocation.getMode() == SINGLE_NODE ? "单节点" : "集群");

                Image image = imageMap.get(imageAllocation.getImageId());
                if (image != null) {
                    imageAllocationTableVO.getImages().add(image.getImageName());
                    imageAllocationTableVO.getFlavors().add(String.format("CPU上限%s核 内存上限%sG",
                            imageAllocation.getCpu(), imageAllocation.getRam()));
                }

                if (imageAllocation.getMode() == CLUSTER) {
                    List<ImageAllocationSlave> imageAllocationSlaveList = imageAllocationSlaveMap.get(contextId);

                    if (CollectionUtils.isNotEmpty(imageAllocationSlaveList)) {
                        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaveList) {
                            Image slaveImage = imageMap.get(imageAllocationSlave.getImageId());
                            if (slaveImage != null) {
                                imageAllocationTableVO.getImages().add(slaveImage.getImageName());
                                imageAllocationTableVO.getFlavors().add(String.format("CPU上限%s核 内存上限%sG",
                                        imageAllocation.getSlaveCpu(), imageAllocation.getSlaveRam()));
                            }
                        }
                    }
                }

                imageAllocationTableVOS.add(imageAllocationTableVO);
            }
        }

        return imageAllocationTableVOS.stream()
                .collect(Collectors.toMap(ImageAllocationTableVO::getContextId, Function.identity()));
    }

    @Override
    public List<Long> getHaveImageTasks(List<Long> taskIds) {

        List<ImageAllocation> imageAllocations = imageAllocationMapperHelper.listByContext(TASK.getCode(), taskIds);
        List<Long> haveImageTasks = imageAllocations.stream().map(ImageAllocation::getContextId).collect(Collectors.toList());

        return haveImageTasks;
    }

    private void insertImage(Image image, Map<Integer, Integer> iconMap) {
        if (!imageMapperHelper.isExistsImageNameInDocker(image.getImageNameInDocker())) {
            Image insertImage = new Image();
            insertImage.setGpu(image.getGpu());
            insertImage.setImageName(image.getImageName());
            insertImage.setImageNameInDocker(image.getImageNameInDocker());
            insertImage.setShared(true);
            insertImage.setImageSource(SYSTEM_PRESET_CODE);
            insertImage.setDescription(image.getDescription());
            insertImage.setIcon(iconMap.get(image.getIcon()));
            imageMapper.insertSelective(insertImage);
        }
    }

    private void insertImageAllocation(Integer contextType, Long contextId, ImageAllocationUpdatePO imageAllocationUpdatePO) {
        ImageAllocation imageAllocation = ImageAllocation.builder()
                .contextType(contextType)
                .contextId(contextId)
                .mode(imageAllocationUpdatePO.getMode())
                .imageId(imageAllocationUpdatePO.getImageId())
                .cpu(imageAllocationUpdatePO.getCpu())
                .ram(imageAllocationUpdatePO.getRam())
                .build();
        if (CLUSTER == imageAllocationUpdatePO.getMode()) {
            imageAllocation.setSlaveCpu(imageAllocationUpdatePO.getSlaveCpu());
            imageAllocation.setSlaveRam(imageAllocationUpdatePO.getSlaveRam());
        }
        imageAllocationMapper.insertSelective(imageAllocation);
    }

    private void updateImageAllocation(ImageAllocation imageAllocation, ImageAllocationUpdatePO imageAllocationUpdatePO) {
        ImageAllocation updateImageAllocation = ImageAllocation.builder()
                .id(imageAllocation.getId())
                .mode(imageAllocationUpdatePO.getMode())
                .imageId(imageAllocationUpdatePO.getImageId())
                .cpu(imageAllocationUpdatePO.getCpu())
                .ram(imageAllocationUpdatePO.getRam())
                .build();
        if (CLUSTER == imageAllocationUpdatePO.getMode()) {
            updateImageAllocation.setSlaveCpu(imageAllocationUpdatePO.getSlaveCpu());
            updateImageAllocation.setSlaveRam(imageAllocationUpdatePO.getSlaveRam());
        } else {
            updateImageAllocation.setSlaveCpu(0);
            updateImageAllocation.setSlaveRam(0);
        }
        imageAllocationMapperExt.updateImageAllocation(updateImageAllocation);
    }

    private void insertImageAllocationSlave(Integer contextType, Long contextId, List<Long> slaveImageIds) {
        for (int roleNumber = 1; roleNumber <= slaveImageIds.size(); roleNumber++) {
            ImageAllocationSlave imageAllocationSlave = ImageAllocationSlave.builder()
                    .contextType(contextType)
                    .contextId(contextId)
                    .imageId(slaveImageIds.get(roleNumber - 1))
                    .roleNumber(roleNumber)
                    .build();
            imageAllocationSlaveMapper.insertSelective(imageAllocationSlave);
        }
    }

    private void updateImageAllocationSlave(Integer contextType, Long contextId, List<Long> slaveImageIds) {
        List<ImageAllocationSlave> imageAllocationSlaves = imageAllocationSlaveMapperHelper.listByContext(contextType, contextId);
        Map<Integer, ImageAllocationSlave> imageAllocationSlaveMap = new HashMap<>();
        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
            imageAllocationSlaveMap.put(imageAllocationSlave.getRoleNumber(), imageAllocationSlave);
        }

        for (int roleNumber = 1; roleNumber <= slaveImageIds.size(); roleNumber++) {
            long imageId = slaveImageIds.get(roleNumber - 1);
            if (imageAllocationSlaveMap.containsKey(roleNumber)) {
                ImageAllocationSlave imageAllocationSlave = imageAllocationSlaveMap.get(roleNumber);
                if (imageId != imageAllocationSlave.getImageId()) {
                    ImageAllocationSlave updateImageAllocationSlave = ImageAllocationSlave.builder()
                            .id(imageAllocationSlave.getId())
                            .imageId(imageId)
                            .build();
                    imageAllocationSlaveMapper.updateByPrimaryKeySelective(updateImageAllocationSlave);
                }
            } else {
                ImageAllocationSlave imageAllocationSlave = ImageAllocationSlave.builder()
                        .contextType(contextType)
                        .contextId(contextId)
                        .imageId(imageId)
                        .roleNumber(roleNumber)
                        .build();
                imageAllocationSlaveMapper.insertSelective(imageAllocationSlave);
            }
        }

        for (ImageAllocationSlave imageAllocationSlave : imageAllocationSlaves) {
            if (imageAllocationSlave.getRoleNumber() > slaveImageIds.size()) {
                imageAllocationSlaveMapperHelper.deleteById(imageAllocationSlave.getId());
            }
        }
    }

    private ImageAllocationCourseVO buildImageAllocationCourseVOS(
            ChapterBasic chapterBasic, Map<Long, ImageAllocation> imageAllocationMap,
            Map<Long, List<ImageAllocationSlave>> imageAllocationSlaveMap, Map<Long, Image> imageMap) {

        ImageAllocationCourseVO imageAllocationCourseVO = ImageAllocationCourseVO.builder()
                .chapterId(chapterBasic.getId())
                .chapterName(chapterBasic.getName())
                .build();

        if (imageAllocationMap.containsKey(chapterBasic.getId())) {
            imageAllocationCourseVO.setAllocated(true);
            ImageAllocation imageAllocation = imageAllocationMap.get(chapterBasic.getId());
            imageAllocationCourseVO.setMode(imageAllocation.getMode());
            if (imageAllocation.getMode() == SINGLE_NODE) {
                setSingleInfo(imageAllocationCourseVO, imageAllocation, imageMap);
            } else {
                setClusterInfo(imageAllocationCourseVO, imageAllocation, imageMap, imageAllocationSlaveMap.get(chapterBasic.getId()));
            }
        } else {
            imageAllocationCourseVO.setAllocated(false);
        }
        return imageAllocationCourseVO;
    }

    private void setSingleInfo(ImageAllocationCourseVO imageAllocationCourseVO, ImageAllocation imageAllocation,
                               Map<Long, Image> imageMap) {

        Image image = imageMap.get(imageAllocation.getImageId());
        if (image != null) {
            imageAllocationCourseVO.setImageName(image.getImageName());
            int imageSource = image.getImageSource();
            if (imageSource == SYSTEM_PRESET_CODE) {
                imageAllocationCourseVO.setImageSource("预置");
            } else {
                imageAllocationCourseVO.setImageSource("教师制作");
            }
        } else {
            imageAllocationCourseVO.setImageName("镜像已删除");
            imageAllocationCourseVO.setImageSource("镜像已删除");
        }
        imageAllocationCourseVO.setFlavor(String.format("CPU上限%s核 内存上限%sG",
                imageAllocation.getCpu(), imageAllocation.getRam()));
    }

    private void setClusterInfo(ImageAllocationCourseVO imageAllocationCourseVO, ImageAllocation imageAllocation,
                                Map<Long, Image> imageMap, List<ImageAllocationSlave> imageAllocationSlaveList) {

        List<String> imageNameList = Lists.newArrayList();
        List<String> imageSourceList = Lists.newArrayList();

        fillListFromImage(imageNameList, imageSourceList, imageMap.get(imageAllocation.getImageId()));
        if (CollectionUtils.isNotEmpty(imageAllocationSlaveList)) {
            imageAllocationSlaveList.forEach(imageAllocationSlave ->
                    fillListFromImage(imageNameList, imageSourceList, imageMap.get(imageAllocationSlave.getImageId())));
        }

        imageAllocationCourseVO.setImageName(StringUtils.join(imageNameList, "/"));
        imageAllocationCourseVO.setImageSource(StringUtils.join(imageSourceList, "/"));
        imageAllocationCourseVO.setFlavor(String.format("主节点：CPU上限%s核 内存上限%sG 从节点：CPU上限%s核 内存上限%sG",
                imageAllocation.getCpu(), imageAllocation.getRam(), imageAllocation.getSlaveCpu(), imageAllocation.getSlaveRam()));
    }

    private void fillListFromImage(List<String> imageNameList, List<String> imageSourceList, Image image) {
        if (image != null) {
            imageNameList.add(image.getImageName());
            int imageSource = image.getImageSource();
            if (imageSource == SYSTEM_PRESET_CODE) {
                imageSourceList.add("预置");
            } else {
                imageSourceList.add("教师制作");
            }
        } else {
            imageSourceList.add("镜像已删除");
            imageSourceList.add("镜像已删除");
        }
    }

}
